import { useCallback, useDeferredValue } from "react"
import { create, StoreApi } from "zustand"
import { genStorageKey } from "./common"
import { CustomError, ErrorCode, ErrorFactory } from "./error"
import _ from "lodash"
import { createLogger } from "./logger"
import { createJSONStorage, persist } from "zustand/middleware"
import { atom, useAtom } from "jotai"
import { useDebounceEffect } from "ahooks"
import { getToken } from "@/utils/cached"

// 創建日誌記錄器
const logger = createLogger("utils/zustand")

/**
 * 創建一個具有數據獲取功能的 Repository Store
 *
 * 這個函數將一個異步數據獲取函數封裝為一個 Zustand store，提供:
 * - 數據緩存和共享
 * - 節流請求避免過於頻繁調用
 * - 可選的持久化存儲
 * - 自動初始化加載
 * - 加載狀態追蹤
 * - 錯誤處理
 *
 * @template Props - API 請求參數的類型
 * @template Result - API 響應數據的類型
 * @param fetcher - 數據獲取函數，返回 Promise<Result>
 * @param options - 配置選項
 * @returns 包含 useRepository hook 和原子狀態的對象
 */
export function createRepository<Props extends object, Result extends object>(
    fetcher: (props?: Props) => Promise<Result>,
    options?: {
        defaultProps?: Props // 默認請求參數
        key?: string // 持久化存儲的鍵名
        persist?: boolean // 是否啟用持久化
        persistOptions?: {
            // 持久化選項
            storage?: Storage // 存儲引擎 (默認為 localStorage)
            expireIn?: number // 過期時間(毫秒)
        }
        throttleTime?: number // 節流時間(毫秒)，默認 300ms
        authRequired?: boolean // 是否需要身份驗證，默認 false
    },
) {
    // 配置默認值
    const _throttle_time = options?.throttleTime ?? 300
    const _is_persist = options?.persist ?? false
    const _storage_key = options?.key ? genStorageKey(options.key) : undefined
    const _storage = options?.persistOptions?.storage ?? localStorage
    const _auth_required = options?.authRequired ?? false

    // 使用 Jotai atoms 管理全局加載狀態
    // 這允許多個組件訂閱共享的加載狀態
    const _state_atoms = {
        loading: atom<boolean>(false),
        fetched: atom<boolean>(false),
    }

    /**
     * 創建節流的數據獲取函數
     * 使用 throttle 確保在指定時間內最多執行一次 API 請求
     * leading: true 確保第一次調用立即執行
     * trailing: false 防止在等待期結束時再次執行
     */
    const throttledFetcher = _.throttle(
        async (props?: Props) => {
            try {
                // 檢查是否需要身份驗證
                if (_auth_required) {
                    const token = getToken()
                    // 檢查 token 是否存在以及是否過期
                    if (!token?.accessToken) {
                        // 拋出認證錯誤
                        throw ErrorFactory.createRepositoryError("Unauthorized")
                    }
                }

                // 使用提供的 fetcher 函數獲取數據
                const result = await fetcher(props ?? options?.defaultProps)
                return result
            } catch (_error) {
                // 直接重拋出認證錯誤
                if ((_error as CustomError)?.code === ErrorCode.REPOSITORY_ERROR) {
                    throw _error
                }

                // 其他錯誤處理保持不變
                const error = _error as Error
                const customError = ErrorFactory.createRepositoryError(error.message)
                throw customError
            }
        },
        _throttle_time,
        { leading: true, trailing: false },
    )

    /**
     * 數據歷史記錄項目的類型定義
     * 包含請求參數、響應數據、錯誤和時間戳
     */
    type DataHistory = {
        props?: Props | null // 請求參數
        data?: Result | null // 響應數據
        error?: CustomError | null // 錯誤信息
        _t: number // 時間戳
    }

    /**
     * Zustand store 的類型定義
     */
    type ZustandStore = {
        history: DataHistory[] // 數據歷史記錄
        actions: {
            // 可用的操作方法
            fetch(props?: Props): Promise<Result>
        }
    }

    type ZustandApi = StoreApi<ZustandStore>

    /**
     * Zustand store 創建函數
     * 定義 store 的初始狀態和操作方法
     */
    const zustandStoreCreator = (set: ZustandApi["setState"]) => {
        async function _fetch(props?: Props) {
            try {
                const result = await throttledFetcher(props)
                // 更新 store 狀態: 將新結果添加到歷史記錄
                const newRecord: DataHistory = {
                    props,
                    data: result,
                    _t: Date.now(),
                    error: null,
                }
                set((prev) => ({
                    ...prev,
                    history: [...prev.history, newRecord],
                }))
                return result
            } catch (error) {
                const newRecord: DataHistory = {
                    props,
                    error: error as CustomError,
                    _t: Date.now(),
                }
                set((prev) => ({
                    ...prev,
                    history: [...prev.history, newRecord],
                }))
                throw error
            }
        }

        const output: ZustandStore = {
            // 數據歷史記錄數組
            history: [],
            // 可用的操作方法
            actions: {
                /**
                 * 獲取數據並更新 store
                 * @param props - 可選的請求參數
                 * @returns 獲取的數據結果
                 */
                fetch: _.throttle(_fetch, _throttle_time, { leading: true, trailing: false }),
            },
        }
        return output
    }

    /**
     * 創建 Zustand store
     * 根據配置決定是否使用持久化中間件
     */
    const useStore =
        _is_persist && _storage_key
            ? create<ZustandStore>()(
                  // 使用 persist 中間件實現持久化
                  persist(zustandStoreCreator, {
                      name: _storage_key,
                      storage: createJSONStorage(() => _storage),
                      // 只持久化 history 部分 並保持最新的 10 筆
                      partialize(state) {
                          logger.debug("partialize", state.history.length)
                          return {
                              history: state.history.slice(-10),
                          }
                      },
                  }),
              )
            : create(zustandStoreCreator)

    /**
     * Repository Hook - 提供組件使用的接口
     *
     * 包括:
     * - 自動數據加載
     * - 加載狀態管理
     * - 錯誤處理
     * - 手動重新獲取方法
     *
     * @param hookOptions - Hook 配置選項
     * @returns 包含數據、狀態和操作方法的對象
     */
    const useRepository = (hookOptions?: {
        autoFetch?: boolean // 是否自動獲取數據 (默認 true)
        props?: Props // 請求參數
    }) => {
        // 從 Zustand store 獲取數據和操作
        const data = useStore((s) => s.history.slice(-1)[0]?.data ?? null)
        const error = useStore((s) => s.history.slice(-1)[0]?.error ?? null)
        const actions = useStore((s) => s.actions)
        const history = useStore((s) => s.history)

        // 使用 Jotai atoms 管理加載狀態
        const [loading, setLoading] = useAtom(_state_atoms.loading)
        const [fetched, setFetched] = useAtom(_state_atoms.fetched)

        // 是否自動獲取數據 (默認為 true)
        const autoFetch = hookOptions?.autoFetch ?? true

        /**
         * 手動獲取數據的方法
         * 會更新加載狀態和完成狀態
         */
        const fetch = useCallback(
            async (props?: Props) => {
                setLoading(true)
                try {
                    const result = await actions.fetch(props)
                    setFetched(true)
                    return result
                } finally {
                    setLoading(false)
                }
            },
            [actions, setFetched, setLoading],
        )

        /**
         * 自動獲取數據的副作用
         * 當組件掛載並且 autoFetch 為 true 時，自動獲取數據
         */
        useDebounceEffect(
            () => {
                logger.debug("autoFetch", autoFetch, fetched)
                if (autoFetch && !fetched) {
                    fetch()
                }
            },
            [fetched],
            { trailing: false, leading: true },
        )

        const deferredLoading = useDeferredValue(loading)
        const deferredFetched = useDeferredValue(fetched)
        const deferredData = useDeferredValue(data)
        const deferredError = useDeferredValue(error)

        // 返回數據、狀態和操作方法
        return {
            data: deferredData, // 最新數據
            state: {
                loading: deferredLoading,
                fetched: deferredFetched,
                error: deferredError,
            },
            fetch, // 手動獲取方法
        }
    }

    // 返回 hook 和狀態原子
    return [useRepository, useStore] as const
}
